#include "Shooter_PCH.h"
#include "Material.h"

/*--------------------------------------------------------------------------*/

Material::Material(const std::string& name) :
	m_frontPolygonMode	(GL_FILL),
	m_backPolygonMode	(GL_FILL),
	m_depthTestEnabled	(GL_FALSE),
	m_depthFunc			(GL_LEQUAL),
	m_blendEnabled		(GL_FALSE),
	m_blendSrc			(GL_ONE),
	m_blendDst			(GL_ONE),
	m_blendEquation		(GL_FUNC_ADD),
	m_cullFace			(GL_FALSE)
{
	m_shader = new GLSLProgram("../Data/glsl/" + name);
}

/*--------------------------------------------------------------------------*/

Material::~Material()
{
	delete m_shader;
}

/*--------------------------------------------------------------------------*/

void Material::Bind()
{
	m_shader->Bind();

	glPolygonMode(GL_FRONT,		m_frontPolygonMode);
	glPolygonMode(GL_BACK,		m_backPolygonMode);
	
	if (m_depthTestEnabled == GL_FALSE)		glDisable(GL_DEPTH_TEST);	else	glEnable(GL_DEPTH_TEST);

	glDepthFunc(m_depthFunc);

	if (m_blendEnabled == GL_FALSE)			glDisable(GL_BLEND);		else	glEnable(GL_BLEND);

	glBlendFunc(m_blendSrc, m_blendDst);
	glBlendEquation(m_blendEquation);

	if (m_cullFace == GL_FALSE)				glDisable(GL_CULL_FACE);	else	glEnable(GL_CULL_FACE);


	for (auto iter = m_uniform.begin(); iter != m_uniform.end(); )
	{
		if (iter->second.m_location == UINT_MAX)
		{
			iter->second.m_location = glGetUniformLocation(m_shader->GetHandle(), iter->first.c_str());
		}

		if (iter->second.m_location == UINT_MAX)
		{
			iter = m_uniform.erase(iter);
			continue;
		}
		else
		{
			Uniform& var = iter->second;
			unsigned int loc = var.m_location;

			void* ptn = NULL;
			memcpy(&ptn, var.m_data, 4);

			int* intptn = (int*) ptn;
			float* fltptn = (float*) ptn;

			int* intval = (int*) var.m_data;
			float* fltval = (float*) var.m_data;

			switch(var.m_type)
			{
			case EUniform::PtnSampler:
				glUniform1i(loc, intptn[0]);
				break;
			case EUniform::PtnFloat1:
				glUniform1f(loc, fltptn[0]);
				break;
			case EUniform::PtnFloat2:
				glUniform2f(loc, fltptn[0], fltptn[1]);
				break;
			case EUniform::PtnFloat3:
				glUniform3f(loc, fltptn[0], fltptn[1], fltptn[2]);
				break;
			case EUniform::PtnFloat4:
				glUniform4f(loc, fltptn[0], fltptn[1], fltptn[2], fltptn[3]);
				break;
			case EUniform::PtnInt1:
				glUniform1i(loc, intptn[0]);
				break;
			case EUniform::PtnInt2:
				glUniform2i(loc, intptn[0], intptn[1]);
				break;
			case EUniform::PtnInt3:
				glUniform3i(loc, intptn[0], intptn[1], intptn[2]);
				break;
			case EUniform::PtnInt4:
				glUniform4i(loc, intptn[0], intptn[1], intptn[2], intptn[3]);
				break;
			case EUniform::PtnFloat4x4:
				glUniformMatrix4fv(loc, 1, GL_FALSE, &fltptn[0]);
				break;
			case EUniform::Sampler:
				glUniform1i(loc, intval[0]);
				break;
			case EUniform::Float1:
				glUniform1f(loc, fltval[0]);
				break;
			case EUniform::Float2:
				glUniform2f(loc, fltval[0], fltval[1]);
				break;
			case EUniform::Float3:
				glUniform3f(loc, fltval[0], fltval[1], fltval[2]);
				break;
			case EUniform::Float4:
				glUniform4f(loc, fltval[0], fltval[1], fltval[2], fltval[4]);
				break;
			case EUniform::Int1:
				glUniform1i(loc, intval[0]);
				break;
			case EUniform::Int2:
				glUniform2i(loc, intval[0], intval[1]);
				break;
			case EUniform::Int3:
				glUniform3i(loc, intval[0], intval[1], intval[2]);
				break;
			case EUniform::Int4:
				glUniform4i(loc, intval[0], intval[1], intval[2], intval[4]);
				break;
			default:
				std::cout << "UNKNOWN UNIFORM TYPE!" << std::endl;
			}

			++iter;
		}
	}
}

void Material::SetUniform( EUniform::Type type, const std::string& name, void* value )
{
	auto& iter = m_uniform.find(name);

	if (iter != m_uniform.end())
	{
		m_uniform[name] = Uniform();
	}

	Uniform& uniform = m_uniform[name];
	uniform.m_type = type;

	switch(type)
	{
	case EUniform::PtnSampler:
		memcpy(uniform.m_data, &value, 4);
		break;
	case EUniform::PtnFloat1:
		memcpy(uniform.m_data, &value, 4);
		break;
	case EUniform::PtnFloat2:
		memcpy(uniform.m_data, &value, 4);
		break;
	case EUniform::PtnFloat3:
		memcpy(uniform.m_data, &value, 4);
		break;
	case EUniform::PtnFloat4:
		memcpy(uniform.m_data, &value, 4);
		break;
	case EUniform::PtnInt1:
		memcpy(uniform.m_data, &value, 4);
		break;
	case EUniform::PtnInt2:
		memcpy(uniform.m_data, &value, 4);
		break;
	case EUniform::PtnInt3:
		memcpy(uniform.m_data, &value, 4);
		break;
	case EUniform::PtnInt4:
		memcpy(uniform.m_data, &value, 4);
		break;
	case EUniform::PtnFloat4x4:
		memcpy(uniform.m_data, &value, 4);
		break;
	case EUniform::Sampler:
		memcpy(uniform.m_data, value, 4);
		break;
	case EUniform::Float1:
		memcpy(uniform.m_data, value, 4);
		break;
	case EUniform::Float2:
		memcpy(uniform.m_data, value, 8);
		break;
	case EUniform::Float3:
		memcpy(uniform.m_data, value, 12);
		break;
	case EUniform::Float4:
		memcpy(uniform.m_data, value, 16);
		break;
	case EUniform::Int1:
		memcpy(uniform.m_data, value, 4);
		break;
	case EUniform::Int2:
		memcpy(uniform.m_data, value, 8);
		break;
	case EUniform::Int3:
		memcpy(uniform.m_data, value, 12);
		break;
	case EUniform::Int4:
		memcpy(uniform.m_data, value, 16);
		break;
	default:
		std::cout << "UNKNOWN UNIFORM TYPE!" << std::endl;
	}
}

/*--------------------------------------------------------------------------*/
